package com.pangus.toolkits.files.operation;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.pangus.toolkits.files.FSContext;
import com.pangus.toolkits.files.http.NetworkClient;
import com.pangus.toolkits.files.http.Response;
import com.pangus.toolkits.files.model.FileInfo;
import com.pangus.toolkits.files.request.Range;
import okhttp3.*;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.List;

import static com.pangus.toolkits.files.constants.ApiUrl.*;

/**
 * @author yingheng chen
 * @description
 * @date 2019/12/3
 */

public class FSFileOperation extends BaseOperation {
    //大文件上传策略
    private static final long DEFAULT_SPLIT_SIZE = 16 * 1024 * 1024;
    private final NetworkClient networkClient;
    private final HttpUrl baseUrl;

    public FSFileOperation(NetworkClient networkClient, FSContext context) {
        super(context);
        this.networkClient = networkClient;
        this.baseUrl = context.getUrl();
    }

    public Response<FileInfo> fileLink(String serverFilePath) throws IOException {
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(GET_FileInfo)
                .addEncodedQueryParameter("path", Base64.encodeUrlSafe(serverFilePath))
                .build();
        Request request = getBaseRequestBuilder()
                .url(url)
                .build();
        return networkClient.call(request, FileInfo.class);
    }

    public Response<List<FileInfo>> listFileLink(String serverDir) throws IOException {

        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(GET_FileList)
                .addQueryParameter("prefix", serverDir)
                .build();
        Request request = getBaseRequestBuilder()
                .url(url)
                .build();
        return networkClient.callList(request, FileInfo.class);
    }

    /**
     * TODO：大文件分片上传
     *
     * @param file      上传文件
     * @param serverDir 服务器路径
     * @return
     * @throws IOException
     */
    public Response<FileInfo> upload(@Nonnull File file, String serverDir) throws IOException {
        Assert.notNull(file);
        String mimeType = FileUtil.getMimeType(file.getPath());
        if (StrUtil.isBlank(mimeType)) {
            mimeType = "application/octet-stream";
        }
        final String fileMd5 = SecureUtil.md5(file);
        if (file.length() < DEFAULT_SPLIT_SIZE) {
            return simpleUpload(file, serverDir, mimeType, fileMd5);
        } else {
            return largeFileUpload(file, serverDir, mimeType, fileMd5);
        }
    }

    /**
     * TODO: 大文件上传
     *
     * @param file
     * @param serverDir
     * @param mimeType
     * @param fileMd5
     * @return
     * @throws IOException
     */
    private Response<FileInfo> largeFileUpload(File file, String serverDir, String mimeType, String fileMd5) throws IOException {
        //check exist?
        Response<FileInfo> exist = this.checkFileExist(fileMd5, serverDir);
        if (exist != null && exist.getData() != null) {
            return exist;
        }
        //TODO:split file and upload

        //TODO:merge
        return simpleUpload(file, serverDir, mimeType, fileMd5);
    }

    public Response<FileInfo> checkFileExist(String fileMd5, String serverDir) throws IOException {
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(GET_MD5CHECK)
                .addQueryParameter("path", Base64.encodeUrlSafe(serverDir))
                .addQueryParameter("md5", fileMd5)
                .build();
        Request request = getBaseRequestBuilder()
                .url(url)
                .get()
                .build();
        return networkClient.call(request, FileInfo.class);

    }

    private Response<FileInfo> simpleUpload(@Nonnull File file, String serverDir, String mimeType, String fileMd5) throws IOException {
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(PUT_UPLOAD)
                .addPathSegments(file.getName())
                .addQueryParameter("path", Base64.encodeUrlSafe(serverDir))
                .addQueryParameter("md5", fileMd5)
                .build();
        RequestBody requestBody = RequestBody.create(MediaType.parse(mimeType), file);
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), requestBody)
                .build();
        Request request = getBaseRequestBuilder()
                .url(url)
                .post(multipartBody)
                .build();
        return networkClient.call(request, FileInfo.class);
    }

    public Response<Boolean> deleteFile(FileInfo info) throws IOException {
        Assert.notNull(info);
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(DEL_FILE)
                .addQueryParameter("id", String.valueOf(info.getId()))
                .build();
        Request request = getBaseRequestBuilder()
                .url(url)
                .delete()
                .build();
        return networkClient.call(request, Boolean.class);

    }

    public Response<ResponseBody> downLoad(FileInfo fileInfo, Range range) throws IOException {
        Assert.notNull(fileInfo);
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegments(GET_Download)
                .addQueryParameter("id", String.valueOf(fileInfo.getId()))
                .build();
        final Request.Builder builder = getBaseRequestBuilder()
                .url(url);
        Request request = null;
        if (range == null || range.equals(Range.ALL)) {
            request = builder.build();
        } else {
            request = builder.header("Range", "bytes=" + range.toString()).build();
        }
        return networkClient.call(request);
    }
}
